use actix_web::{
  error as ActixError, http::StatusCode, HttpResponse, Result as ActixResult,
};
use serde::Serialize;
use sqlx::error::Error as SQLxError;

#[derive(Debug, Serialize)]
pub enum CustomError {
  DBError(String),
  InternalError(String),
  NotFound(String),
  InvalidInput(String),
}

#[derive(Debug, Serialize)]
pub struct CustomResponseError {
  error_message: String,
}

impl CustomError {
  fn error_response(&self) -> String {
    match self {
      Self::DBError(msg) => {
        eprintln!("Database error occurred: {:?}", msg);
        "Database error.".into()
      }
      Self::InternalError(msg) => {
        eprintln!("Server error occurred: {:?}", msg);
        "Internal server error.".into()
      }
      Self::NotFound(msg) => {
        eprintln!("Not found error occurred: {:?}", msg);
        msg.into()
      }
      Self::InvalidInput(msg) => {
        eprintln!("Invalid input: {:?}", msg);
        msg.into()
      }
    }
  }
}

impl std::fmt::Display for CustomError {
  fn fmt(
    &self,
    f: &mut std::fmt::Formatter,
  ) -> ActixResult<(), std::fmt::Error> {
    // write!(f, "{}", self)
    write!(f, "")
  }
}

impl ActixError::ResponseError for CustomError {
  fn status_code(&self) -> StatusCode {
    match self {
      Self::DBError(_) | Self::InternalError(_) => {
        StatusCode::INTERNAL_SERVER_ERROR
      }
      Self::NotFound(_) => StatusCode::NOT_FOUND,
      Self::InvalidInput(_) => StatusCode::BAD_REQUEST,
    }
  }

  fn error_response(&self) -> HttpResponse {
    HttpResponse::build(self.status_code()).json(CustomResponseError {
      error_message: self.error_response(),
    })
  }
}

impl From<ActixError::Error> for CustomError {
  fn from(err: ActixError::Error) -> Self {
    CustomError::InternalError(err.to_string())
  }
}

impl From<SQLxError> for CustomError {
  fn from(err: SQLxError) -> Self {
    CustomError::DBError(err.to_string())
  }
}
